%%% Copyright [heng] [litaocheng@gmail.com] 
%%% Licensed under the Apache License, Version 2.0 (the "License");
%%% you may not use this file except in compliance with the License. 
%%% You may obtain a copy of the License at 
%%%    http://www.apache.org/licenses/LICENSE-2.0 
%%% Unless required by applicable law or agreed to in writing, 
%%% software distributed under the License is distributed on an "AS IS" BASIS, 
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
%%% See the License for the specific language governing permissions and 
%%% limitations under the License. 

%%% iterator util
%%% usage:
%%%   Iter = make_iter(prev_fun, next_fun, state),
%%%   {Next , Iter2} = next(Iter),
%%%  {Next2, Iter3} = next(Iter2).
%%% 
-module(heng_iter).
-author('litaocheng@gmail.com').
-vsn('0.1').
-include("heng_master.hrl").

-export([make_iter/3]).
-export([next/1, prev/1]).
-export([foldl/3, foldr/3]).

-record(iter,
        {  
            %type = normal :: 'normal' | 'cyclic', % 
            next = nil :: 'nil' | fun(),
            prev = nil :: 'nil' | fun(),
            state
        }).

%% @doc get the next item
next(#iter{next = nil}) ->
    {error, edirection};
next(Iter = #iter{next = Next, state = State}) ->
    case Next(State) of
        nil -> % to the end
            {nil, Iter};
        {V, State2} ->
            {{value, V}, Iter#iter{state = State2}}
    end.
    
%% @doc get the item in the backward
prev(#iter{prev = nil}) ->
    {error, edirection};
prev(Iter = #iter{prev = Prev, state = State}) ->
    case Prev(State) of
        nil -> % to the end
            {nil, Iter};
        {V, State2} ->
            {{value, V}, Iter#iter{state = State2}}
    end.

%-spec foldl(Fun :: fun(), Acc :: term(), Iter :: iter()) -> 
%    term().
foldl(Fun, Acc, Iter) when is_function(Fun, 2) ->
    {V, Iter2} = next(Iter),
    foldl0(Fun, Acc, V, Iter2).

foldl0(_Fun, Acc, nil, _Iter) ->
    Acc;
foldl0(Fun, Acc, {value, V}, Iter) ->
    Acc2 = Fun(V, Acc),
    {V2, Iter2} = next(Iter),
    foldl0(Fun, Acc2, V2, Iter2).
    
foldr(Fun, Acc, Iter) when is_function(Fun, 2) ->
    {V, Iter2} = prev(Iter),
    foldr0(Fun, Acc, V, Iter2).

foldr0(_Fun, Acc, nil, _Iter) ->
    Acc;
foldr0(Fun, Acc, {value, V}, Iter) ->
    Acc2 = Fun(V, Acc),
    {V2, Iter2} = prev(Iter),
    foldr0(Fun, Acc2, V2, Iter2).

%% @doc 'fward' equal 'forward' , 'bid' equal 'bidirectional'
make_iter(Next, Prev, State) 
        when is_function(Next) andalso is_function(Prev)
            %andalso ((Type =:= 'normal') or (Type =:= 'cyclic')) 
            ->
    #iter{%type = Type,
          next = Next,
          prev = Prev,
          state = State
          }.

-ifdef(debug).
%% @doc some test
basic_test_() ->
    Data = ["begin", "one", "two", "there", "four", "end"],
    ExpR1 = "begin one two there four end",
    ExpR2 = "end four three two one begin",
    Iter = make_iter(fun listnext/1, fun listprev/1, {0, 0, length(Data), Data}),
    R = 
    ?MODULE:foldl(fun(Str, Acc) ->
        [Str, $  | Acc]
        end,
        [],
        Iter),
    Str = lists:flatten(lists:reverse(R)),
        
    Iter2 = make_iter(fun listnext/1, fun listprev/1, {length(Data)+1, 1, length(Data), Data}),
    R2 =
    ?MODULE:foldr(fun(E, Acc) ->
        [E, $  | Acc]
        end,
        [],
        Iter2),
    Str2 = lists:flatten(lists:reverse(R2)),
    [
        ?_assert(Str = ExpR1),
        ?_assert(Str2 = ExpR2)
    ].    
    
listnext({Last, _First, Last, _Data}) ->
    nil;
listnext({Pos, First, Last, Data}) ->
    Pos2 = Pos + 1,
    V = lists:nth(Pos2, Data),
    {V, {Pos2, First, Last, Data}}. 
    
listprev({0, _First, _Last, _Data}) ->
    nil;
listprev({First, First, _Last, _Data}) ->
    nil;
listprev({Pos, First, Last, Data}) ->
    Pos2 = Pos - 1,
    V = lists:nth(Pos2, Data),
    {V, {Pos2, First, Last, Data}}.     
-endif.
